(function() {
    if (window.ubank) return;

    window.ubank = {

        _count: 0,
        _init: false,
        _jsMethods: {
            _obs: {

            }
        },
        _jsAsyncMethods: {
            _obs: {

            }
        },
        _cb: {
            // save api callback
        },
        _event: {
            // save event callback
        },
        _call: function(method, object) {
            console.log('method:' + method);
            var ret = '';
            if (object === null || object === undefined) {
                object = {};
            }

            if (typeof object.success == 'function') {
                var cbName = 'ubankcb' + this._count++;
                this._cb[cbName] = object.success;
                object['success'] = cbName;
            }
            if (typeof object.fail == 'function') {
                var cbName = 'ubankcb' + this._count++;
                this._cb[cbName] = object.fail;
                object['fail'] = cbName;
            }
            if (typeof object.progress == 'function') {
                var cbName = 'ubankcb' + this._count++;
                this._cb[cbName] = object.progress;
                object['progress'] = cbName;
            }

            //if in webview that dsBridge provided, call!
            if (window._ubankCore) {
                ret = _ubankCore.call(method, JSON.stringify(object));
            } else if (window._ubankCoreIOS) {
                ret = prompt("_ubankCore=" + method, JSON.stringify(object));
            }
            var res = JSON.parse(ret || '{}');
            if (res.code == 0) {
                return res.data;
            } else {
                if (res.code !== undefined) {
                    var event = 'method:' + method + '\n' + 'code:' + res.code + '\n' + 'msg:' + res.msg;
                    console.log(event);
                    alert(event);
                }
                return res.data;
            }
        },

        _register: function(name, fun, asyn) {
            console.log('register name:' + name + " fun:" + fun + " asyn:" + asyn);
            var q = asyn ? this._jsAsyncMethods : this._jsMethods;
            if (!this._init) {
                this._init = true;
                //notify native that js apis register successfully on next event loop
                setTimeout(function() {
                    window.ubank._call("internal.dispatchStart");
                }, 0)
            }
            if (typeof fun == "object") {
                q._obs[name] = fun;
            } else {
                q[name] = fun
            }
        },

        _unRegister: function(name, asyn) {
            console.log('unRegister name:' + name + " asyn:" + asyn);
            var q = asyn ? this._jsAsyncMethods : this._jsMethods;
            if (typeof fun == "object") {
                delete q._obs[name];
            } else {
                delete q[name];
            }
        },

        _handleMessage: function(info) {
            console.log('Native call JS method:' + info.method + " args:" + info.data);
            var arg = JSON.parse(info.data);
            var ret = {
                id: info.callbackId,
                complete: true
            }
            var f = this._jsMethods[info.method];
            var af = this._jsAsyncMethods[info.method];


            var callSyn = function(f, ob) {
                ret.data = f.apply(ob, arg);
                window.ubank._call("internal.returnValue", ret)
            }
            var callAsyn = function(f, ob) {
                arg.push(function(data, complete) {
                    ret.data = data;
                    ret.complete = complete !== false;
                    window.ubank._call("internal.returnValue", ret);
                })
                f.apply(ob, arg);
            }
            if (f) {
                console.log("find method sync:" + f);
                callSyn(f, this._jsMethods);
            } else if (af) {
                console.log("find method async:" + af);
                callAsyn(af, this._jsAsyncMethods);
            } else {
                //with namespace
                console.log("find method none");
                var name = info.method.split('.');
                if (name.length < 2) return;
                var method = name.pop();
                var namespace = name.join('.')
                var obs = this._jsMethods._obs;
                var ob = obs[namespace] || {};
                var m = ob[method];
                if (m && typeof m == "function") {
                    callSyn(m, ob);
                    return;
                }
                obs = this._jsAsyncMethods._obs;
                ob = obs[namespace] || {};
                m = ob[method];
                if (m && typeof m == "function") {
                    callAsyn(m, ob);
                    return;
                }
            }
        },


        // SDK start
        canIUse: function(name, type) {

            return this._call("internal.hasNativeMethod", {
                name: name,
                type: type || "all"
            });
        },

        registerPageEvent: function(object) {
            if (object === null || object === undefined) {
                object = {};
            }
            if (typeof object.onLoad == 'function') {
                this._register('onLoad', object.onLoad, false);
            }
            if (typeof object.onShow == 'function') {
                this._register('onShow', object.onShow, false);
            }
            if (typeof object.onHide == 'function') {
                this._register('onHide', object.onHide, false);
            }
            if (typeof object.onUnload == 'function') {
                this._register('onUnload', object.onUnload, false);
            }
            if (typeof object.onReload == 'function') {
                this._register('onReload', object.onReload, false);
            }
            if (typeof object.onRefresh == 'function') {
                this._register('onRefresh', object.onRefresh, false);
            }
        },

        registerEvent: function(name, fun) {
            return this._register(name, fun, false);
        },

        unRegisterEvent: function(name) {
            return this._unRegister(name, false);
        },

        sendEvent: function(object) {
            return this._call('messager.sendEvent', object);
        },

        sendTmlEvent: function(object) {
            return this._call('messager.sendTmlEvent', object);
        },


        getDeviceInfoSync: function() {
            return this._call("system.getDeviceInfoSync");
        },
        getDeviceLegacyInfoSync: function() {
            return this._call("system.getDeviceLegacyInfoSync");
        },
        getWindowInfoSync: function() {
            return this._call("system.getWindowInfoSync");
        },
        getSystemInfoSync: function() {
            return this._call("system.getSystemInfoSync");
        },
        getSystemSettingSync: function() {
            return this._call("system.getSystemSettingSync");
        },
        getAppBaseInfoSync: function() {
            return this._call("system.getAppBaseInfoSync");
        },
        getNetworkInfoSync: function() {
            return this._call("system.getNetworkInfoSync");
        },
        navigateToWebApp: function(object) {
            if (typeof object.onResult == 'function') {
                var cbName = 'ubankcb' + this._count++;
                this._cb[cbName] = object.onResult;
                object['onResult'] = cbName;
            }
            this._call("route.navigateToWebApp", object);
        },
        navigateBackWebApp: function(object) {
            this._call("route.navigateBackWebApp", object);
        },

        navigateTo: function(object) {
            this._call("route.navigateTo", object);
        },
        navigateBack: function(object) {
            this._call("route.navigateBack", object);
        },
        redirectTo: function(object) {
            this._call("route.redirectTo", object);
        },
        openTmlApp: function(object) {
            if (typeof object.onResult == 'function') {
                var cbName = 'ubankcb' + this._count++;
                this._cb[cbName] = object.onResult;
                object['onResult'] = cbName;
            }
            this._call("route.openTmlApp", object);
        },
        navigateToApp: function(object) {
            this._call("route.navigateToApp", object);
        },
        switchTab: function(object) {
            this._call("route.switchTab", object);
        },
        dial: function(object) {
            this._call("route.dial", object);
        },

        request: function(object) {
            this._call("network.request", object);
        },

        requestImage: function(object) {
            this._call("network.requestImage", object);
        },

        exchangeKey: function(object) {
            this._call("network.exchangeKey", object);
        },

        downloadFile: function(object) {
            this._call("network.downloadFile", object);
        },

        uploadFile: function(object) {
            this._call("network.uploadFile", object);
        },


        setStorageSync: function(key, value, encrypt) {
            var object = {};
            object.key = key;
            object.value = value;
            object.encrypt = encrypt;
            this._call("storage.setStorageSync", object);
        },
        setMemorySync: function(key, value, encrypt) {
            var object = {};
            object.key = key;
            object.value = value;
            object.encrypt = encrypt;
            this._call("storage.setMemorySync", object);
        },
        getMemorySync: function(key, encrypt) {
            var object = {};
            object.key = key;
            object.encrypt = encrypt;
            return this._call("storage.getMemorySync", object);
        },
        getStorageSync: function(key, encrypt) {
            var object = {};
            object.key = key;
            object.encrypt = encrypt;
            return this._call("storage.getStorageSync", object);
        },
        removeStorageSync: function(key) {
            var object = {};
            object.key = key;
            return this._call("storage.removeStorageSync", object);
        },
        removeMemorySync: function(key) {
            var object = {};
            object.key = key;
            return this._call("storage.removeMemorySync", object);
        },
        reload: function(object) {
            return this._call("route.reload", object);
        },

        showToast: function(object) {
            return this._call("widget.showToast", object);
        },
        showDialog: function(object) {
            if (typeof object.onDialogClick == 'function') {
                var cbName = 'ubankcb' + this._count++;
                this._cb[cbName] = object.onDialogClick;
                object['onDialogClick'] = cbName;
            }

            return this._call("widget.showDialog", object);
        },
        showLoading: function(object) {
            return this._call("widget.showLoading", object);
        },
        hideLoading: function(object) {
            return this._call("widget.hideLoading", object);
        },
        showActionSheet: function(object) {
            if (typeof object.onMenuItemClick == 'function') {
                var cbName = 'ubankcb' + this._count++;
                this._cb[cbName] = object.onMenuItemClick;
                object['onMenuItemClick'] = cbName;
            }
            return this._call("widget.showActionSheet", object);
        },
        setNavigationBar: function(object) {
            if (typeof object.onMenuItemClick == 'function') {
                this.registerEvent("onMenuItemClick", object.onMenuItemClick);
            }
            return this._call("widget.setNavigationBar", object);
        },
        setNavigationBarColor: function(object) {
            return this._call("widget.setNavigationBarColor", object);
        },
        setNavigationBarTitle: function(object) {
            return this._call("widget.setNavigationBarTitle", object);
        },
        setNavigationBarLogo: function(object) {
            return this._call("widget.setNavigationBarLogo", object);
        },
        setNavigationBarMenu: function(object) {
            return this._call("widget.setNavigationBarMenu", object);
        },

        // hardware
        openKeyboard: function(object) {
            if (object.type == 0) {
                this._call("keyboard.openKeyboard", object);
            } else {
                if (typeof object.textChange == 'function') {
                    this.registerEvent("textChange", object.textChange);
                }
                if (typeof object.passCheck == 'function') {
                    this.registerEvent("passCheck", object.passCheck);
                }
                if (typeof object.keyboardDone == 'function') {
                    this.registerEvent("keyboardDone", object.keyboardDone);
                }
                if (typeof object.done == 'function') {
                    this.registerEvent("done", object.done);
                }
                if (typeof object.keyDelete == 'function') {
                    this.registerEvent("keyDelete", object.keyDelete);
                }
                if (typeof object.keyInput == 'function') {
                    this.registerEvent("keyInput", object.keyInput);
                }
                if (object.type == 1) {
                    this._call("authcode.openKeyboard", object);
                } else {
                    this._call("securekeyboard.openKeyboard", object);
                }

            }

        },
        closeKeyboard: function(type) {
            if (type == 1) {
                this._call("authcode.closeKeyboard");
            } else if (type == 2) {
                this._call("securekeyboard.closeKeyboard");
            } else {
                this._call("keyboard.closeKeyboard");
            }

        },
        isOpen: function(type) {
            if (type == 1) {
                return this._call("authcode.isOpen");
            } else if (type == 2) {
                return this._call("securekeyboard.isOpen");
            } else {
                return this._call("keyboard.isOpen");
            }
        },
        clearValue: function(type) {
            if (type == 1) {
                this._call("authcode.clearValue");
            } else {
                this._call("securekeyboard.clearValue");
            }
        },
        requestFocus: function(focus) {
            var object = {};
            object.focus = focus;
            this._call("keyboard.requestFocus", object);
        },


        startFace: function(object) {
            if (object.type == 'eyecool') {
                this._call("eyefacedetect.startFace", object);
            } else {
                this._call("alifacedetect.startFace", object);
            }

        },
        startBankCard: function(object) {
            var type = object.type;
            object.params = {};
            object.params.cardType = '2';
            object.params.addWater = object.water;
            if (type == 'hanvon') {
                this._call("hanvonocr.startPreview", object);
            } else {
                this._call("heheocr.startPreview", object);
            }
        },
        startIdCard: function(object) {
            var type = object.type;
            object.params = {};
            if (object.front) {
                object.params.cardType = '0';
            } else {
                object.params.cardType = '1';
            }
            object.params.addWater = object.water;
            if (type == 'hanvon') {
                this._call("hanvonocr.startPreview", object);
            } else {
                this._call("heheocr.startPreview", object);
            }

        },
        biometrics: {
            isSupportFaceID: function() {
                return window.ubank._call("biometricsauth.isSupportFaceID");
            },
            isSupportTouchID: function() {
                return window.ubank._call("biometricsauth.isSupportTouchID");
            },
            noEnrolled: function() {
                return window.ubank._call("biometricsauth.noEnrolled");
            },
            reset: function() {
                window.ubank._call("biometricsauth.resetBiometrics");
            },
            cancel: function() {
                window.ubank._call("biometricsauth.cancelBiometrics");
            },
            startTouchID: function(object) {
                window.ubank._call("biometricsauth.startTouchID", object);
            },
            startFaceID: function(object) {
                window.ubank._call("biometricsauth.startFaceID", object);
            },
        },
        shareToQQ: function(object) {
            this._call("share.shareToQQ", object);
        },
        shareToWeixin: function(object) {
            this._call("share.shareToWeixin", object);
        },
        showShareDialog: function(object) {
            this._call("share.showShareDialog", object);
        },
        reportEvent: function(object) {
            this._call("analytics.reportEvent", object);
        },
        //日志上报 data string 逗号格式
        reportEventLegacy: function(data) {
            var object = {};
            object.log = data;
            this._call("analytics.reportEventLegacy", object);
        },
        //日志上报 data json tag格式
        traceEvent: function(object) {
            this._call("analytics.traceEvent", object);
        },
        scanCode: function(object) {
            this._call("qrcode.scanCode", object);
        },
        encryptData: function(object) {
            this._call("encrypt.encryptData", object);
        },
        decryptData: function(object) {
            this._call("encrypt.decryptData", object);
        },
        getAppAssetConfigSync: function() {
            return this._call("config.getAppAssetConfigSync");
        },
        getAppConfigSync: function() {
            return this._call("config.getAppConfigSync");
        },
        updateAppConfig: function(object) {
            this._call("config.updateAppConfig", object);
        },
        getWebAppInfoSync: function(url) {
            var object = {};
            object.url = url;
            return this._call("config.getWebAppInfoSync", object);
        },
        getWebAppListSync: function() {
            return this._call("config.getWebAppListSync");
        },

        limitSceenShot: function(limit, msg1, msg2) {
            var object = {};
            object.enable = !limit;
            object.msg1 = msg1;
            object.msg2 = msg2;
            this._call("screen.setScreenShot", object);
        },

        addCalendarEvent: function(object) {
            this._call("calendar.addCalendarEvent", object);
        },

        //旧接口
        setupGroup: function(object) {
            this._call("discard.setupGroup", object);
        },
        setImgNetRequestParams: function(object) {
            this._call("discard.setImgNetRequestParams", object);
        },
        addToken: function(key, value) {
            var object = {};
            object.key = key;
            object.value = value;
            this._call("discard.addToken", object);
        },

        setAppResult: function(str) {
            var object = {};
            object.result = str;
            this._call("route.setAppResult", object);
        },

        // extra API
        addSystemEvent: function(name, fun) {
            var object = {};
            object.name = name;
            this._event[name] = fun;
            this._call('internal.addEvent', object);
        },

        removeSystemEvent: function(name) {
            var object = {};
            object.name = name;
            this._call('internal.removeEvent', object);
            delete this._event.name;
        },


        photo: function(object) {
            this._call("picker.photo", object);
        },

        camera: function(object) {
            this._call("picker.camera", object);
        },

        readAppConfigSync: function(key) {
            var object = {};
            object.key = key;
            return this._call("config.readAppConfigSync", object);
        },

        writeAppConfigSync: function(key, value) {
            var object = {};
            object.key = key;
            object.value = value;
            return this._call("config.writeAppConfigSync", object);
        },

        getLocation: function(object) {
            this._call('location.getLocation', object);
        },

        showPictureDialog: function(object) {
            this._call('widget.showPictureDialog', object);
        },
        showAdvDialog: function(object) {
            this._call('widget.showAdvDialog', object);
        },

        enablePullDownRefresh: function(enable) {
            var object = {};
            object.enable = enable;
            this._call('widget.enablePullDownRefresh', object);
        },

        setRefreshStyle: function(object) {
            this._call('widget.setRefreshStyle', object);
        },
        stopRefresh: function(object) {
            this._call('widget.stopRefresh', object);
        },

        setClipboardData: function(object) {
            this._call("clipboard.setClipboardData", object);
        },
        getClipboardData: function(object) {
            this._call("clipboard.getClipboardData", object);
        },

        readFile: function(object) {
            this._call("storage.readFile", object);
        },

        saveToGallery: function(object) {
            this._call("storage.saveToGallery", object);
        },

        moveToBack: function(object) {
            this._call("route.moveToBack", object);
        },

        openTinyWebPage: function(object) {
            this._call("route.openTinyWebPage", object);
        },

        setStatusBarContentLight: function(light) {
            var object = {};
            object.contentLight = light;
            return this._call("widget.setStatusBarStyle", object);
        },

        // Permission
        hasPermissionSync: function(object) {
            return this._call("permission.hasPermissionSync", object);
        },

        requestPermission: function(object) {
            this._call("permission.requestPermission", object);
        },

        goSettingSync: function(object) {
            this._call("permission.goSettingSync", object);
        },

        hasNotificationSync: function(object) {
            return this._call("permission.hasNotificationSync", object);
        },

        goNotificationSettingSync: function(object) {
            this._call("permission.goNotificationSettingSync", object);
        },

        chooseContact: function(object) {
            this._call("picker.chooseContact", object);
        },

        // new face
        face: {
            supportSDK: function() {
                return window.ubank._call("face.supportSDK");
            },
            getInitData: function(object) {
                window.ubank._call("face.getInitData", object);
            },
            startFace: function(object) {
                window.ubank._call("face.startFace", object);
            }
        },

        // DES
        encryptDESData: function(object) {
            this._call("encrypt.encryptDESData", object);
        },

        isWebAppContainer: function(object) {
            if (window._ubankCore || window._ubankCoreIOS) {
                return true;
            } else {
                return false;
            }
        },

        // only android
        setWindowSoftInputMode: function(object) {
            this._call("widget.setWindowSoftInputMode", object);
        }

    }

})();